﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ConceptsFramework.ConceptResults;
using ConceptsFramework.AbstractConceptElements;
using ConceptsFramework.Containers.Global;

namespace DPL.LogicalScript
{
    /// <summary>
    /// DynamicConceptSetPipe matches automatically an equation left hand 
    /// and right hand variable. In FUNC(A)=B, A is the left variable 
    /// and B is the right variable. Changing A automatically changes B,
    /// and A and B are two ResultSets. Changing B can remove A items 
    /// if they're not compliant
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DynamicConceptSetPipe<T> : IDynamicConceptSetPipe where T : AbstractRelationship
    {
        private Dictionary<AbstractBindableConcept, List<AbstractBindableConcept>> PipeRelationshipCache;
        private List<ResultSet> BoundResultSets;
        
        public DynamicConceptSetPipe()
        {
            PipeRelationshipCache = new Dictionary<AbstractBindableConcept, List<AbstractBindableConcept>>();
            BoundResultSets = new List<ResultSet>();
            
        }

        /// <summary>
        /// Update all dependant ResultSet
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RightVariable_CurrentSet_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            foreach (ResultSet aResultSet in BoundResultSets)
            {
                ReduceLeftConceptsFromRightConcepts(e);
            }
        }

        public void BindExternalResultSetToRightVariable(ResultSet externalSet)
        {
            externalSet.CurrentSet.CollectionChanged+=BoundCurrentSet_CollectionChanged;
            BoundResultSets.Add(externalSet);
        }

        // Apply left hand reduction from the right hand side changes.
        private void BoundCurrentSet_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            ReduceLeftConceptsFromRightConcepts(e);
        }

        ResultSet _LeftVariable;
        public void SetLeftVariable(ref ResultSet LeftVariable)
        {
            if (LeftVariable == null)
            {
                _LeftVariable = new ResultSet();
            }
            else
            {
                _LeftVariable = LeftVariable;
                LeftVariable.CurrentSet.CollectionChanged += CurrentSet_CollectionChanged;
            }
        }

        ResultSet _RightVariable;
        public void SetRightVariable(ref ResultSet RightVariable)
        {
            if (RightVariable == null)
            {
                  _RightVariable = new ResultSet(GetRightConceptsFromLeftConceptsList(_LeftVariable.CurrentSet.ToList()));
                  RightVariable = _RightVariable;
                   
            }
            else
            {
                _RightVariable = RightVariable;
            }
            _RightVariable.CurrentSet.CollectionChanged += RightVariable_CurrentSet_CollectionChanged;
        }

        /// <summary>
        /// Only delete leftVariable items that aren't compliant with the current right hand variable removal;
        /// for example, if F(anItem1) = anItem2 and andItem2 is removed from the right hand side, then 
        /// anItem1 should be removed from the left hand item.
        /// </summary>
        /// <param name="e"></param>
        private void ReduceLeftConceptsFromRightConcepts(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {

            foreach (var aDeletedConceptFromRightHand in e.OldItems)
            {
                List<AbstractBindableConcept> leftVariableConceptsToRemove = new List<AbstractBindableConcept>();
                foreach (var aLeftItem in _LeftVariable.CurrentSet)
                {
                    foreach (var aConcept in GetRightConceptsFromSingleLeftConcept(aLeftItem))
                    {
                        if (aConcept == aDeletedConceptFromRightHand)
                            leftVariableConceptsToRemove.Add(aConcept);
                    }
                }
                _LeftVariable.CurrentSet.RemoveRange(leftVariableConceptsToRemove);
            }
        }


        private void StrongUpdateRightHandFromLeftHand(ResultSet Variable, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            List<AbstractBindableConcept> rightConceptsToRemove = GetRightConceptsFromLeftConceptsList((List<AbstractBindableConcept>)e.OldItems);
            foreach (AbstractBindableConcept aRightConceptToRemove in rightConceptsToRemove)
            {
                if (Variable.CurrentSet.Contains(aRightConceptToRemove))
                    Variable.CurrentSet.Remove(aRightConceptToRemove);
            }


            List<AbstractBindableConcept> newRightConcepts = GetRightConceptsFromLeftConceptsList((List<AbstractBindableConcept>)e.NewItems);
            foreach (AbstractBindableConcept aNewRightConcept in newRightConcepts)
            {
                if (!Variable.CurrentSet.Contains(aNewRightConcept))
                    Variable.CurrentSet.Add(aNewRightConcept);
            }
        }

   
        


        void CurrentSet_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            StrongUpdateRightHandFromLeftHand(_RightVariable, e);
        }

       

        private List<AbstractBindableConcept> GetRightConceptsFromLeftConceptsList(List<AbstractBindableConcept> leftConcepts)
        {
            List<AbstractBindableConcept> rightConcepts = new List<AbstractBindableConcept>();
            if (leftConcepts != null)
            {
                foreach (var aConcept in leftConcepts)
                {
                    rightConcepts.AddRange(GetRightConceptsFromSingleLeftConcept(aConcept));
                }
            }
            return rightConcepts.Distinct().ToList();
        }


        private List<AbstractBindableConcept> GetRightConceptsFromSingleLeftConcept(AbstractBindableConcept leftConcept)
        {
            List<AbstractRelationship> allConceptsRelationships = GlobalRelationshipsSingletonContainer.Instance.RelationshipContainer.GetRelationships(leftConcept.RelationshipsMap);
            List<AbstractBindableConcept> rightConcepts = new List<AbstractBindableConcept>();
            foreach (var currentRightConcepts in (from aRelationship in allConceptsRelationships
                                                  where aRelationship.GetType() == typeof(T)
                                                  select aRelationship.RightConcepts))
            {
                rightConcepts.AddRange(currentRightConcepts);
            }
            return rightConcepts.Distinct().ToList();
        }
        
    }
}
